using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Paddle : MonoBehaviour
{
    private BoxCollider2D boxCollider2D;

    private Vector2 dir;
    private float halfWidth;
    private Vector2 originPos;
    private bool isStop;
    private BuffController buffController;
    private Locomotion locomotion;

    [SerializeField] private float moveSpeed = 30f;
    [SerializeField] private float maxBounceAngle = 75f;
    [SerializeField] private float offsetBounce = 5f;
    [SerializeField] private float spinFactor;
    [SerializeField] private Vector3 wideScale;
    [SerializeField] private Vector3 shortScale;
    [SerializeField] private bool isRotate;

    private void Awake() {
        boxCollider2D = GetComponent<BoxCollider2D>();
        buffController = GetComponent<BuffController>();
        locomotion = GetComponent<Locomotion>();
    }

    private void Start() {
        halfWidth = boxCollider2D.bounds.size.x / 2;
        originPos = transform.position;
    }

    private void OnEnable() {
        EventHandler.AddListener(Events.ResetGame, ResetPaddle);
        EventHandler.AddListener(Events.NextLevel, ()=> isStop = true);
        EventHandler.AddListener(Events.EndGame, () => isStop = true);
        EventHandler.AddListener<PropType, float>(Events.GetBuff, BuffAction);
    }

    private void OnDisable() {
        EventHandler.RemoveListener(Events.ResetGame);
        EventHandler.RemoveListener(Events.NextLevel);
        EventHandler.RemoveListener(Events.EndGame);
        EventHandler.RemoveListener(Events.GetBuff);
    }

    private void Update() {
        if(isStop)
            return;
        
        dir.x = Input.GetAxisRaw("Horizontal");
    }

    private void FixedUpdate() {
        // rigidbody2D.velocity = dir.normalized * moveSpeed;
        locomotion.SetVelocity(dir.normalized * moveSpeed);
    }

    private void ResetPaddle()
    {
        isStop = false;
        transform.position = originPos;
    }

    private void BuffAction(PropType type, float duration)
    {
        bool hasBuff = buffController.isHasBuff(type);

        if(!hasBuff)
        {
            switch(type)
            {
                case PropType.Wide:
                    transform.localScale = wideScale;
                    break;
                
                case PropType.Short:
                    transform.localScale = shortScale;
                    break;
            }
        }

        buffController.UpdateBuffState(type, duration);
        float realDuration = buffController.GetBuffTimeStampByType(type) - Time.time;
        TimeManager.Instance.GetTimer(realDuration, ()=>{CancelBuff(type);});
    }

    private void CancelBuff(PropType type)
    {
        bool isInBuffDuring = buffController.isInBuffDuring(type);

        if(isInBuffDuring)
            return;
        
        switch(type)
        {
            case PropType.Wide:
                transform.localScale = Vector3.one;
                break;
            
            case PropType.Short:
                transform.localScale = Vector3.one;
                break;
        }

        buffController.SubBuffByType(type);
    }

    private void OnCollisionEnter2D(Collision2D other) {
        if(other.gameObject.TryGetComponent<Ball>(out var ball))
        {
            if(!isRotate)
            {
                Vector3 paddlePos = transform.position;
                Vector2 hitPos = other.GetContact(0).point;
                
                float offset = paddlePos.x - hitPos.x;
                float currentAngle = Vector2.SignedAngle(Vector2.up, ball.GetCurrentVelocity());
                float bounceAngle = (offset / halfWidth) * maxBounceAngle;
                float newAngle = Mathf.Clamp(currentAngle + bounceAngle, -maxBounceAngle, maxBounceAngle);

                if(Mathf.Abs(newAngle) < 0.1f)
                    newAngle += offsetBounce;

                Quaternion rotation = Quaternion.AngleAxis(newAngle, Vector3.forward);
                ball.SetVelocity(rotation * Vector2.up * ball.GetCurrentVelocity().magnitude);
            }
            // ==============================================================
            else
            {
                Vector2 relativeVelocity = other.relativeVelocity;
            
                float currentAngle = Vector2.SignedAngle(Vector2.up, ball.GetCurrentVelocity());
                float newAngle = currentAngle;
                float spinSpeed = relativeVelocity.magnitude * spinFactor;
                
                if(spinSpeed > 0)
                {
                    float spinDir = Mathf.Sign(relativeVelocity.x);
                    newAngle = Mathf.Clamp(newAngle + spinDir * spinFactor, -maxBounceAngle, maxBounceAngle);
                    if(ball.TryGetComponent<Rigidbody2D>(out Rigidbody2D rb))
                    {
                        rb.angularVelocity = spinDir * spinSpeed;
                    }
                }
                Quaternion rotation = Quaternion.AngleAxis(newAngle, Vector3.forward);
                ball.SetVelocity(rotation * Vector2.up * ball.GetCurrentVelocity().magnitude);
            }
        }
    }
}
